package com.wzy.portfolio.utils;

import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzy.portfolio.model.FundBaseDto;
import com.wzy.portfolio.model.FundDetails;
import com.wzy.portfolio.model.FundManagerDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class FundUtil {

    /**
     * 获取全国所有基金基础数据
     * @return
     */
    public static List<FundBaseDto> findFundCode(){
        List<FundBaseDto> list = new ArrayList<>();
        String url = "https://fund.eastmoney.com/js/fundcode_search.js";
        String body = HttpRequest.get(url).execute().body();
        body = body.substring(0,body.length()-1).replaceAll("var r = ","");
        JSONArray arr = JSONArray.parseArray(body);
        for(int i=0;i<arr.size();i++){
            JSONArray tmpArr = arr.getJSONArray(i);
            String name = tmpArr.getString(2);
            FundBaseDto dto = new FundBaseDto(name, tmpArr.getString(0));
            list.add(dto);
        }
        return list;
    }

    /**
     * 从天天基金网返回的详情中解析收益率
     * @param content
     * @return
     * @throws JsonProcessingException
     */
    public static FundBaseDto extractReturns(String name,String code,String content){
        Pattern pattern = Pattern.compile("var\\s+syl_1n=\"([^\"]+)\";.*?var\\s+syl_6y=\"([^\"]+)\";.*?var\\s+syl_3y=\"([^\"]+)\";.*?var\\s+syl_1y=\"([^\"]+)\";");
        Matcher matcher = pattern.matcher(content);
        try {
            if (matcher.find()) {
                FundBaseDto dto = new FundBaseDto(name,code);
                dto.setRate365(Double.parseDouble(matcher.group(1)));
                dto.setRate180(Double.parseDouble(matcher.group(2)));
                dto.setRate90(Double.parseDouble(matcher.group(3)));
                dto.setRate30(Double.parseDouble(matcher.group(4)));
                Double fundValue = extractFundValue(content);
                dto.setFundValue(fundValue);
                dto.setFundType(getFundType(code));//基金类型
                return dto;
            }else {
                return null;
            }
        }catch (Exception e){
            return null;
        }
    }

    /**
     * 获取基金的类型
     * @param code
     * @return
     */
    public static String getFundType(String code ){
        String url = "https://fund.eastmoney.com/"+code+".html";
        String html = HttpRequest.get(url).execute().body();
        String decodedHtml = StringEscapeUtils.unescapeHtml4(html).replaceAll("\\u00A0", " ").replaceAll("\\s+", " ");
        Pattern pattern = Pattern.compile( "类型[:：]\\s*<a\\b[^>]+>([^<]+)</a>\\s*[|‖│]\\s*([\\u4e00-\\u9fa5-]+)(?=\\s|<|$)" );
        Matcher matcher = pattern.matcher(decodedHtml);
        if (matcher.find()) {
            return matcher.group(1) + "-" + matcher.group(2);
        }
        return "未知";
    }

    /**
     * 提取市值
     * @param content JS 格式字符串
     * @return 最后一个 y 值，找不到则返回 null
     */
    public static Double extractFundValue(String content) throws Exception {
        double value = 0;
        Pattern pattern = Pattern.compile("var\\s+Data_fluctuationScale\\s*=\\s*(\\{.*?\\});", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(content);
        if (matcher.find()) {
            String jsonStr = matcher.group(1);
            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readTree(jsonStr);
            JsonNode seriesArray = rootNode.path("series");
            if (seriesArray.isArray() && seriesArray.size() > 0) {
                JsonNode lastItem = seriesArray.get(seriesArray.size() - 1);
                value = lastItem.path("y").asDouble();
            }
        }
        return value;
    }

    /**
     * 提取基金经理相关信息
     * @param jsCode
     * @param varName Data_holderStructure
     * @return
     */
    public static List<FundManagerDto> extractVar(String jsCode, String varName) {
        List<FundManagerDto> list = new ArrayList<>();
        try {
            int start = jsCode.indexOf("var " + varName + " ="); // 定位变量起始位置
            if (start == -1) return list;
            start = jsCode.indexOf('=', start) + 1;
            ArrayDeque<Character> stack = new ArrayDeque<>();
            StringBuilder sb = new StringBuilder();
            boolean inString = false;
            char prevChar = 0;
            for (int i = start; i < jsCode.length(); i++) {
                char c = jsCode.charAt(i);
                if (c == '/' && jsCode.charAt(i+1) == '*') break;// 跳过注释
                if (c == '"' && prevChar != '\\') inString = !inString; // 处理字符串内的转义
                prevChar = c;
                if (!inString) {// 括号匹配核心逻辑
                    if (c == '{' || c == '[') stack.push(c);
                    if ((c == '}' && stack.peek() == '{') ||
                            (c == ']' && stack.peek() == '[')) stack.pop();
                }
                sb.append(c);
                if (c == ';' && stack.isEmpty()) break; // 终止条件：遇到分号且栈为空
            }
            // 清理结果
            String rsp = sb.toString().replaceAll(";$", "").replaceAll("\\\\u003c", "<").trim();
            JSONArray arr = JSONArray.parseArray(rsp);
            for(int m=0;m<arr.size();m++){
                JSONObject obj = arr.getJSONObject(m);
                FundManagerDto dto = new FundManagerDto();
                dto.setManagerId(obj.getString("id"));
                dto.setManagerName(obj.getString("name"));
                dto.setStar(obj.getInteger("star"));
                dto.setWorkTime(obj.getString("workTime"));
                JSONArray data = obj.getJSONObject("power").getJSONArray("data");
                dto.setExperienceRating(data.getString(0));
                dto.setProfitabilityScore(data.getString(1));
                dto.setMarketJudgmentScore(data.getString(4));
                list.add(dto);
            }
            return list;
        }catch (Exception e){
            log.error("基金经理信息解析异常:{}", e.getMessage(), e);
            return list;
        }
    }

    /**
     * 获取某个基金的实时数据
     * jsonpgz({"fundcode":"005094","name":"万家臻选混合A","jzrq":"2025-07-11","dwjz":"2.7009","gsz":"2.6893","gszzl":"-0.43","gztime":"2025-07-14 15:00"});
     * @param code
     * @return
     */
    public static FundDetails fundZdfDetails(String code) {
        FundDetails dto = new FundDetails();
        String url = "http://fundgz.1234567.com.cn/js/"+code+".js";
        try {
            String rsp = HttpRequest.get(url).execute().body();
            rsp = rsp.replaceAll("jsonpgz\\(","").replaceAll("\\);","");
            JSONObject obj = JSON.parseObject(rsp);
            dto.setFundCode(obj.getString("fundcode"));
            dto.setFundName(obj.getString("name"));
            dto.setMonitorDate(obj.getString("gztime").split(" ")[0]);
            dto.setYczdf(obj.getString("gszzl"));
            dto.setDwjz(obj.getString("dwjz"));
            return dto;
        }catch (Exception e){
            log.error("查询基金({})异常:{}", code, e.getMessage(), e);
            return null;
        }
    }

    /**
     * 从fundf10.eastmoney.com网站获取基金历史数据
     * 该网站不能获取当天的数据
     * @param code
     * @param name
     * @param sdate
     * @param edate
     * @return
     */
    public static List<FundDetails> fetchFundHistory(String code,String name, String sdate, String edate) {
        List<FundDetails> list = new ArrayList<>();
        int currentPage = 1;
        int totalPages = 1; // 初始设为1，第一次请求后更新真实值
        while (currentPage <= totalPages) {
            String html = HttpUtil.get(StrUtil.format( "https://fundf10.eastmoney.com/F10DataApi.aspx?type=lsjz&code={}&sdate={}&edate={}&per={}&page={}", code, sdate, edate, 20, currentPage ));
            try {
                if (currentPage == 1) {
                    String pagesStr = ReUtil.get("pages:(\\d+)", html, 1);
                    totalPages = Integer.parseInt(pagesStr);
                }
                Element table = Jsoup.parse(html).selectFirst("table"); // 解析表格数据
                if (table != null) {
                    Elements rows = table.select("tr");
                    for (int i = 1; i < rows.size(); i++) { // 跳过标题行
                        Elements cols = rows.get(i).select("td");
                        if (cols.size() >= 7) {
                            FundDetails dto = new FundDetails();
                            dto.setFundCode(code);
                            dto.setFundName(name);
                            dto.setMonitorDate(cols.get(0).text());
                            String zdf = cols.get(3).text().replaceAll("%", "");
                            if( StringUtils.isNotBlank(zdf)){
                                dto.setZdf(zdf);
                                dto.setDwjz(cols.get(1).text());
                                list.add(dto);
                                log.info("基金信息:{}", dto);
                            }else {
                                log.error("{},涨跌幅解析异常:{}", name, JSON.toJSONString(cols));
                            }
                        }
                    }
                }
                Thread.sleep(1000);
            } catch (Exception e) {
                log.error("{},第:{}页解析异常,报文:{},异常信息:{}", name, currentPage, html, e.getMessage(), e);
            }
            currentPage++;
        }
        return list;
    }

}
